#include "precompiled.h"

namespace IcyCrystal {
	void Parser::computeArraySize(Namespace* currentNamespace, Type* rootType ){
		// Find the array portion of the type
		while( rootType && rootType->primitiveType != PRIM_TYPE_ARRAY && rootType->extra.pointTo )
			rootType = rootType->extra.pointTo;

		if( rootType->primitiveType != PRIM_TYPE_ARRAY ){
			// Reached root type.
			return;
		}

		// Go to the right end of the type.
		if( rootType ){
			if( rootType->extra.pointTo )
				computeArraySize(currentNamespace, rootType->extra.pointTo);

			// Our size is elements * child size.
			rootType->size = rootType->arrayAccess.constNumElems * rootType->extra.pointTo->size;
		}
	}

	void Parser::validateArrayInitializer(Namespace* currentNamespace, Type* arrayType, SyntaxTree* argumentsTree){
		// Two posibilities: Array - Non Array
		if( argumentsTree->expressionReturnType->primitiveType == PRIM_TYPE_ARRAY ){
			argumentsTree->expressionReturnType->extra.pointTo = arrayType->extra.pointTo;
		}
		
		if(argumentsTree->nodeType == NODE_SYMBOL && arrayType->primitiveType == PRIM_TYPE_ARRAY && argumentsTree->nodeFlags == NODE_IS_ARRAY_INITIATOR){
			SyntaxTreeList* args = argumentsTree->extra.arrayInitArguments;
			// Check count:
			int officialCount = arrayType->arrayAccess.constNumElems;
			int givenCount = args->size();

			if( officialCount != givenCount ){
				throw ParserException("Excess elements in array not equal to array declaration","");
			}

			// Check elements:
			SyntaxTreeList::iterator it;
			for(it = args->begin(); it!=args->end(); it++){
				validateArrayInitializer(currentNamespace, arrayType->extra.pointTo, *it);
			}
		}
		// Other posibility: non-array
		else {
			// Check both types
			Type* nextLType;
			if( arrayType->extra.pointTo && argumentsTree->expressionReturnType->extra.pointTo)
				nextLType = arrayType->extra.pointTo;
			else
				nextLType = arrayType;

			if( nextLType->primitiveType != argumentsTree->expressionReturnType->primitiveType){
				SyntaxTree fake;
				fake.nodeType = NODE_ASSIGN;
				fake.left = new SyntaxTree();
				fake.left->expressionReturnType = nextLType;
				fake.right = new SyntaxTree();
				fake.right->expressionReturnType = argumentsTree->expressionReturnType;
				promoteAndConvertOp(&fake);
			}

			// Other than that, we're fine.
		}
	}

	bool Parser::arrayIsVLA(Type* rootType){
		if( rootType  && rootType->primitiveType == PRIM_TYPE_ARRAY){
			return (rootType->arraySize == ARRAY_ELEM_DYN) || arrayIsVLA( rootType->extra.pointTo );
		}
		return false;
	}

	Type* Parser::getArrayInitType (Namespace* currentNamespace, Type* rootType , bool isFirst){
		Token* c = lexer.getNextToken();
		Token* k;
		Type* ret = new Type;


		if( TIS_LSQUARE(c) ){
			k = lexer.getReadAheadToken();
			if( TIS_RSQUARE(k) ){
				lexer.getNextToken();
				// Only allow the first dimension to be unknown.
				if( !isFirst ){
					// Error!
					delete ret;
					throw ParserException( "Can not compute elements of array." );
				}
				// We do not yet know the number of elements.
				ret->primitiveType = PRIM_TYPE_ARRAY;
				ret->arraySize = ARRAY_ELEM_WAIT;
			}
			else {
				// We know the number of elements.
				SyntaxTree* arrayNum = getNextExpression(currentNamespace);
				if( ! NODE_IS_INT(arrayNum) && currentNamespace->scope == SCOPE_GLOBAL)
					throw ParserException("CONSTANT", "NON_CONSTANT", lexer.fileReader.getCurrentLine());
				
				ret->primitiveType = PRIM_TYPE_ARRAY;
				
				if( NODE_IS_CONST(arrayNum ) ){
					ret->arraySize = ARRAY_ELEM_CONST;
					// Extract the value manually.
					if( NODE_IS_INT(arrayNum) ){
						ret->arrayAccess.constNumElems = arrayNum->inner.nodeToken.value.intValue;
					}
					else {
						throw ParserException("INT", "NON_INT", lexer.fileReader.getCurrentLine());
					}
				}
				else{
					ret->arraySize = ARRAY_ELEM_DYN;
					ret->arrayAccess.runtimeExpr = arrayNum;
				}

				

				// Check for the final RSQUARE
				c = lexer.getNextToken();
				if( !TIS_RSQUARE(c))
					throw ParserException("RSQUARE",c->tokenTypeToString(), lexer.fileReader.getCurrentLine());
			}
			k = lexer.getReadAheadToken();

			if( TIS_LSQUARE (k) ){
				// Get the type!
				Type* forwardType = getArrayInitType(currentNamespace, rootType, false);
				ret->extra.pointTo = forwardType;
			}
			else {
				// This is the last declaration. We point to the root type.
				ret->extra.pointTo = rootType;
			}
		}

		return ret;
	}

	SyntaxTree* Parser::getArrayAccess(Namespace* currentNamespace, Type* parentType, Symbol* rootSymbol){
		Token* c = lexer.getNextToken();
		Token* k;
		SyntaxTree* parentRet = new SyntaxTree;

		if( TIS_LSQUARE(c) ){
			k = lexer.getReadAheadToken();

			if( TIS_RSQUARE(k) ){
				// Error, we need an expression here.
				throw ParserException("EXPRESSION", k->tokenTypeToString(), lexer.fileReader.getCurrentLine());
			}

			// Get the expression in it.

			SyntaxTree* accessExpr = getNextExpression(currentNamespace);

			// Check rightsquare
			c = lexer.getNextToken();
			if( !TIS_RSQUARE(c) ){
				throw ParserException("RSQUARE",c->tokenTypeToString(), lexer.fileReader.getCurrentLine());
			}

			parentRet->nodeType = NODE_ARRAY_ACCESS;
			// Create the symbol.
			Symbol* tempSymb = new Symbol;
			tempSymb->scope = SCOPE_TEMP;
			tempSymb->level = currentNamespace->level;
			// "Peel" a layer off the parent.
			tempSymb->type = *parentType->extra.pointTo;
			parentRet->expressionReturnType = &tempSymb->type;
			parentRet->inner.sym = tempSymb;

			// Attach the root Symbol
			if( rootSymbol ){
				parentRet->left = new SyntaxTree();
				parentRet->left->nodeType = NODE_SYMBOL;
				parentRet->left->expressionReturnType = &rootSymbol->type;
				parentRet->inner.sym = rootSymbol;
				rootSymbol = NULL;
			}
			
			/**
			* Help out the compiler by computing a constant array access
			*/
			if( NODE_IS_CONST(accessExpr) ){
				parentRet->accessType = ARRAY_ELEM_CONST;
				if( NODE_IS_INT(accessExpr)) {
					parentRet->extra.constArrayAccess = accessExpr->inner.nodeToken.value.intValue;
					delete accessExpr;
				}
			}
			else if (TYPE_IS_INT(accessExpr->expressionReturnType)){
				parentRet->accessType = ARRAY_ELEM_DYN;
				parentRet->extra.arrayAccess = accessExpr;
			}
			else {
				// Invalid!
				// TODO: Delete all those things at the top.
				throw ParserException("integer", accessExpr->expressionReturnType->primitiveTypeToString(), lexer.fileReader.getCurrentLine());
			}
		}

		// Check for another layer
		k = lexer.getReadAheadToken();
		if( TIS_LSQUARE(k) ){
			/**
			* Link this type to the type after
			* (moreLayer of (parentRet of ... of(rootSymbol))..)
			*/
			SyntaxTree* moreLayer = getArrayAccess(currentNamespace, parentType->extra.pointTo, rootSymbol);
			moreLayer->left = parentRet;
			parentRet = moreLayer;
		}

		return parentRet;
	}
}